# !/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time : 2022/6/26 14:20
# @Author : xyjiang1216
# @Email : jiangxuyan@nudt.edu.cn
# @File : map_tbl_linker.py
# @Version: planner_builder 1.0
import socket

from lib.tsn_builder_lib import host_to_net
import xml.etree.ElementTree as EleTree

from tsn_builder_struct.map_tbl_structure import MapTbl, MapTblEachNode, MapEntry_union


def link_map_tbl(node,
                 map_tbl,
                 reg_addr):
    # 找到该节点对应的映射表
    # print('link_map_tbl')
    node_id = node['src_node']
    tbl_each_node = []
    for entry in map_tbl.tbl_set:
        if node_id == entry.node_id:
            tbl_each_node = entry.tbl_each_node
            break

    if not tbl_each_node:
        return []

    base_addr = reg_addr.map_tbl['addr']
    width = reg_addr.map_tbl['width']

    # print(len(tbl_each_node))

    dic = []  # 返回值
    entry_index = 0
    for entry in tbl_each_node:
        addr = base_addr + entry_index * width
        entry_index = entry_index + 1
        addr_str = host_to_net(addr)

        value1 = entry.byte.entry1
        value1_str = host_to_net(value1)

        value2 = entry.byte.entry2
        value2_str = host_to_net(value2)

        value3 = entry.byte.entry3
        value3_str = host_to_net(value3)

        value4 = entry.byte.entry4
        value4_str = host_to_net(value4)

        value5 = entry.byte.entry5
        value5_str = host_to_net(value5)

        value6 = entry.byte.entry6
        value6_str = host_to_net(value6)

        value7 = entry.byte.entry7
        value7_str = host_to_net(value7)

        value8 = entry.byte.entry8
        value8_str = host_to_net(value8)

        addr_value = {
            'addr': addr_str,
            'value': [value1_str, value2_str, value3_str, value4_str,
                      value5_str, value6_str, value7_str, value8_str]
        }
        dic.append(addr_value)

    return dic


def _get_map_tbl(map_tbl_xml):
    map_set = []
    root = EleTree.parse(map_tbl_xml)
    node_ele_set = root.findall('node')
    for node_ele in node_ele_set:
        map_dic = {}
        node_id = node_ele.findtext('node_id')
        map_dic.update({'node_id': int(node_id)})
        map_dic.update({'flow_set': []})
        entry_ele_set = node_ele.findall('entry')
        for entry_ele in entry_ele_set:
            entry_value_set = list(entry_ele)
            flow_dic = {}
            for each_entry in entry_value_set:
                if each_entry.tag == 'five_tuple_mask':
                    flow_dic.update({each_entry.tag: int(each_entry.text, 2)})
                elif each_entry.tag == 'dst_ip' or each_entry.tag == 'src_ip':
                    text = int.from_bytes(socket.inet_aton(each_entry.text), 'big')
                    flow_dic.update({each_entry.tag: text})
                else:
                    flow_dic.update({each_entry.tag: int(each_entry.text)})
            map_dic['flow_set'].append(flow_dic)
        map_set.append(map_dic)
    return map_set


def _init_map_tbl(map_set):
    # 初始化MapTbl对象
    map_tbl = MapTbl()

    for tbl in map_set:
        node_id = tbl['node_id']
        tbl_each_node = MapTblEachNode(node_id)

        flow_set = tbl['flow_set']
        for flow in flow_set:
            # 将每条流填入对应的映射表项
            map_entry = MapEntry_union()
            map_entry.bit.valid = flow['valid']
            map_entry.bit.reserved1 = flow['reserved']
            map_entry.bit.flow_id = flow['flow_id']
            map_entry.bit.five_tuple_mask = flow['five_tuple_mask']
            map_entry.bit.ip_protocol = flow['ip_protocol']
            map_entry.bit.dport = flow['dst_port']
            map_entry.bit.sport = flow['src_port']
            map_entry.bit.dip = flow['dst_ip']
            map_entry.bit.sip = flow['src_ip']
            map_entry.bit.dip = flow['dst_ip']
            map_entry.bit.ipv = flow['priority']
            map_entry.bit.redundant_flag = flow['redundant']
            map_entry.bit.dbufid = flow['dbufid']
            map_entry.bit.reserved2 = flow['reserved']
            map_entry.bit.reserved3 = flow['reserved']
            map_entry.bit.reserved4 = flow['reserved']

            tbl_each_node.append_entry(map_entry)

        map_tbl.append_tbl(tbl_each_node)
    return map_tbl


def read_map_tbl(map_tbl_xml):
    map_set = _get_map_tbl(map_tbl_xml)
    # print('DEBUG')
    [print(entry) for entry in map_set]
    map_tbl = _init_map_tbl(map_set)
    return map_tbl


def main():
    map_tbl_xml = '../output_xml/tbl_xml/map_tbl.xml'
    read_map_tbl(map_tbl_xml)


if __name__ == '__main__':
    main()
